# -*- coding: utf-8 -*-
from IPosicionable import IPosicionable 
from logico.problemas.objetos.Objeto import Objeto
class ITener(object):
	
	"""
	Interfaz que es implementada por Objetos contenedores cuyo contenido es visible (por ejemplo, contenedores de vidrio). La idea de esta interfaz es que guarda una referencia a objetos que posee, y cuando se cambian algunas de sus propiedades, los objetos contenidos se ven afectados (como abrir y mover).
	@since: 4/14/2011
	@version: 1.0
	"""
	
	__elementos={}
	""" Diccionario de elemenos sostenidos """
	
	__maxPeso=0
	""" Tolerancia que indica un maximo teorico de peso a sostener """
	
	__sosteniendo=0
	""" Flag para saber si esta o no sosteniendo algun objeto """
	
	__corrimiento=0
	""" ??? """
	
	__niveles={}
	""" Niveles (coordenadas en y) sobre los que se pueden colocar los objetos contenidos """
	
	def __init__(self, capacidad=0, elementos={}, corrimiento=50, niveles=[0]):
		"""
		Constructor
		@type	self: ITener
		@param	self: referencia al ITener actual
		
		@type	capacidad: int
		@param	capacidad: cantidad que determina un maximo de elementos a contener, ya sea por peso o por tamanio.
		
		@type	elementos: dictionary
		@param	elementos: elementos contenidos, y su relacion nombre --> objeto
		
		@type	corrimiento: int
		@param	corrimiento: ???
		"""
		self.__elementos=elementos
		self.__maxPeso=capacidad
		self.__corrimiento=corrimiento
		self.actualizarPosiciones()
		self.__niveles=niveles
		self.actualizarContenidos()
		
	def tener(self, objeto, x=0, y=0):
		"""
		Constructor
		@type	self: ITener
		@param	self: referencia al ITener actual
		
		@type	objeto: Objeto
		@param	objeto:	objeto a sostener
		"""
		self.__add(objeto, x, y)
		
		
	def soltar(self, nombreObjeto):
		"""
		Permite dejar de sostener un objeto indicado por su nombre
		@type	self: ITener
		@param	self: referencia al ITener actual
		
		@type	nombreObjeto: string
		@param	nombreObjeto: nombre del obeto que se soltara
		"""
		return self.__remove(nombreObjeto)

	def mostrarTodo(self):
		"""
		Hace visibles los objetos contenidos.
		@type	self: ITener
		@param	self: referencia al ITener actual
		"""
		for o in self.__elementos.values():
			try:
				o.mostrar()
			except:
				pass
			
	def mostrar(self, objeto):
		"""
		Hace visible al objeto seleccionado
		@type	self: ITener
		@param	self: referencia al ITener actual
		
		@type	objeto: string
		@param	objeto: nombre del obeto que se mostrara
		"""
		self.__elementos[objeto].mostrar()
		
	def ocultarTodo(self):
		"""
		Hace invisibles a todos los objetos contenidos.
		@type	self: ITener
		@param	self: referencia al ITener actual
		"""
		for o in self.__elementos.values():
			try:
				o.ocultar()
			except:
				pass
	
	def ocultar(self, objeto):
		"""
		Hace invisible al objeto contenido seleccionado.
		@type	self: ITener
		@param	self: referencia al ITener actual
		
		@type	objeto: string
		@param	objeto: nombre del obeto que se ocultara
		"""
		self.__elementos[objeto].ocultar()

	def getObjeto(self, objeto):
		"""
		Permite obtener un Objeto a partir de su nombre, si este se encuentra sostenido
		@type	self: ITener
		@param	self: referencia al ITener actual
		
		@type	objeto: string
		@param	objeto: nombre del obeto que se devolvera
		"""
		return self.__elementos[objeto]

	def existeObjeto(self, objeto):
		"""
		Indica si un objeto dado esta o no siendo sostenido
		@type	self: ITener
		@param	self: referencia al ITener actual
		
		@type	objeto: Objeto
		@param	objeto: objeto a probar su existencia
		"""
		return self.__elementos.has_key(objeto)

	def getKeys(self):
		"""
		Obtiene los nombres de los objetos sostenidos
		@type	self: ITener
		@param	self: referencia al ITener actual
		"""
		return self.__elementos.keys()
	
	def getObjetos(self):
		"""
		Permite obtener todos los Objetos almacenados
		@type	self: ITener
		@param	self: referencia al ITener actual
		"""
		return self.__elementos.values()
	
	def getElementos(self):
		"""
		Permite obtener la relacion nombre --> objeto de los sostenidos
		@type	self: ITener
		@param	self: referencia al ITener actual
		"""
		return self.__elementos
	
	def setElementos(self, elementos):
		"""
		Asigna los elementos contenidos (relacion nombre --> Objeto)
		@type	self: ITener
		@param	self: referencia al ITener actual
		
		@type	elementos: dictionary
		@param	elementos: conjunto de elementos contenidos
		"""
		self.__elementos=elementos
		
	def getDiccionarioObjetos(self):
		"""
		Permite obtener el conjunto de elementos sostenidos (relacion nombre --> objeto)
		@type	self: ITener
		@param	self: referencia al ITener actual
		"""
		lista={}
		#recorremos todos los objetos de la escena
		for o in self.__elementos:
			
			# Agregar relacion a lista
			lista.update({o: self.__elementos[o]})
			
			#es instancia de IContener?
			if(isinstance(self.__elementos[o], ITener)):
				lista.update(self.__elementos[o].getDiccionarioObjetos())
				
		return lista
		
	def getCorrimiento(self):
		"""
		Devuelve el corrimiento de este contenedor
		@type	self: ITener
		@param	self: referencia al ITener actual
		"""
		return self.__corrimiento
		
	def __add(self, objeto, x = 0, y = 0):
		"""
		Aniade un objeto al conjunto de los sostenidos
		@type	self: ITener
		@param	self: referencia al ITener actual
		
		@type	objeto: Objeto
		@param	objeto: objeto a sostener
		"""
		if (self.__sosteniendo+objeto.getPeso())<=self.__maxPeso:
			objeto.setShowInX(x)
			objeto.setShowInY(y)
			
			#Actualiza indicador de que se encuentra contenido
			if(isinstance(objeto, Objeto)):
				objeto.estaContenido=True
				
			self.__elementos[objeto.getNombre()]=objeto
			self.__sosteniendo = self.__sosteniendo + objeto.getPeso()
			if(isinstance(self, IPosicionable) and isinstance(objeto, IPosicionable)):
				objeto.setPosicion([self.getX(), self.getY()])
				# objeto.setPosicion([self.getX()+(len(self.__elementos)*self.__corrimiento)+x, self.getY()+y])
				
			
			return self.__elementos
		else:
			raise Exception("NOOO! YA NO AGUANTO MAS!")
			return None 
	
	def __remove(self, nombre):
		"""
		Elimina una relacion nombre --> Objeto entre los sostenidos
		@type	self: ITener
		@param	self: referencia al ITener actual
		
		@type	nombre: string
		@param	nombre: nombre del obeto que se borrara
		"""
		objeto=self.__elementos[nombre]
		#Actualiza indicador de que se encuentra contenido
		if(isinstance(objeto, Objeto)):
			objeto.estaContenido=False
		self.__sosteniendo=self.__sosteniendo - objeto.getPeso()
		del self.__elementos[nombre]
		self.actualizarPosiciones()
		
		return objeto

	def actualizarPosiciones(self):
		"""
		?????
		@type	self: ITener
		@param	self: referencia al ITener actual
		"""
		if(isinstance(self, IPosicionable)):
			x=self.getX()
			y=self.getY()
			print self.__elementos
			print self.getNombre()
			for o in self.__elementos.values():
				#o.setX(x)
				#o.setY(y)
				if(o.getNombre()!=self.getNombre()):
					o.setPosicion([x, y])
					x=x+self.__corrimiento
				
	def getNiveles(self):
		"""
		Devuelve los niveles de superficie de este contenedor
		@type	self: ITener
		@param	self: referencia al ITener actual
		"""
		return self.__niveles
	
	def getNivel(self, n):
		"""
		Devuelve el nivel de superficie n de este contenedor
		@type	self: ITener
		@param	self: referencia al ITener actual
		
		@type	n:	int
		@param	n: numero de nivel a retornar
		"""
		return self.__niveles[n]
	
	def actualizarContenidos(self):
		"""
		Actualiza los indicadores de si esta contenido el objeto
		@type	self: ITener
		@param	self: referencia al ITener actual

		"""
		for objeto in self.__elementos.values():
			if(isinstance(objeto, Objeto)):
				objeto.estaContenido=True